Tutorial: Writing a custom HIView

E) Implementing support for drag-and-drop

The support for drag-and-drop is done by handling four events: kEventControlDragEnter, kEventControlDragWithin, kEventControlDragLeave, and kEventControlDragReceive.

This is very straightforward and, as usual, after adding those four events to the event list that we register in GetHICustomViewClass, we add the four cases in the function Internal_HICustomViewHandler. We will also add two more fields to our private data as we did when we implemented the custom tracking:

typedef struct
   {
   HIViewRef view;                             // our view

   Boolean trackOn;                            // tracking information
   HIPoint trackSpot;

   HIViewTrackingAreaRef trackRef;             // tracking area

   Boolean dragOn;                             // drag-and-drop information
   HIPoint dragSpot;
   }
HICustomViewData;

And slightly modify the kEventHIObjectInitialize handler to turn dragging on:

case kEventHIObjectInitialize:
   {
   // always begin kEventHIObjectInitialize by calling through to the previous handler
   status = CallNextEventHandler(inHandlerCallRef, inEvent);
   require_noerr(status, InitializeExit);
   
   // if that succeeded, do our own initialization
   status = HIViewNewTrackingArea(myData->view, NULL, 0, &myData->trackRef);
   require_noerr(status, InitializeExit);

   status = SetControlDragTrackingEnabled(myData->view, true);
   require_noerr(status, InitializeExit);

InitializeExit:
   break;
   }

When implementing the kEventControlDragEnter handler, we must ensure the proper behavior by returning correctly the right value in the kEventParamControlWouldAcceptDrop parameter. The three other drag-related handlers will be called only if the kEventControlDragEnter handler returns a ‘true’ value in that parameter and the handler itself returns a status of noErr. In real-life custom HIViews, you should test the contents of the drag pasteboard for a flavor that you can accept: if you find one then return ‘true’ in the parameter kEventParamControlWouldAcceptDrop, ‘false’ if not. In this example, to keep it simple, it accepts all drags and tests for the flavor (text) only in the kEventControlDragReceive handler.

case kEventControlDragEnter:
   {
   Boolean accept = true;
   status = SetEventParameter(inEvent, kEventParamControlWouldAcceptDrop, typeBoolean, sizeof(accept), &accept);
   require_noerr(status, ControlDragEnterExit);
   
   myData->dragOn = true;

ControlDragEnterExit:
   break;
   }

Look at the implementation of the three other handlers by using the project located in the folder “6_Drag_and_Dropping”.

The last thing we have to do is to add the dragging spot drawing in our kEventControlDraw handler:

// if we're dragging then we also draw the drag spot and the focus ring
if (myData->dragOn)
   {
   bounds = CGRectInset(viewBounds, 2, 2);
   HIThemeDrawFocusRect(&bounds, true, context, kHIThemeOrientationNormal);

   CGContextBeginPath(context);
   CGContextAddArc(context, myData->dragSpot.x, myData->dragSpot.y, 10, 0, 2 * pi, 1);
   CGContextClosePath(context);
   CGContextSetRGBFillColor(context, 0.7, 0.7, 0, 0.8);
   CGContextSetRGBStrokeColor(context, 0, 0.7, 0, 0.8);
   CGContextDrawPath(context, kCGPathFillStroke);
   }

And you will see the following dragging:



Next Page

Previous Page

Return to Main Page